#include "MovingObject.h"
#include "../SGD Wrappers/SGD_GraphicsManager.h"
#include "Game.h"
#include "AnimationSystem.h"
#include "TileSystem.h"
#include <random>
#include "stdafx.h"
#include "Emitter.h"
#include "ParticleSystem.h"
#define FLASHDURATION 1.0f
#define FLASHTIMER 0.1f

MovingObject::MovingObject() : Listener(this)
{
	RegisterForEvent("TILECOLLISION");
	RegisterForEvent("LIFESTEAL");
	RegisterForEvent("DOT");
	RegisterForEvent("SLOW");
	RegisterForEvent("STUN");
	RegisterForEvent("KNOCKBACK");

	Enemy* tempEnemy = dynamic_cast<Enemy*>(this);
	Player* tempPlayer = dynamic_cast<Player*>(this);

	// set enemy flash duration / invulerability to .25 seconds
	if (tempEnemy)
	{
		flashDuration = FLASHDURATION;
		flashDuration *= .25f;
	}

	// set player flash duration / invulerability to 1.0 seconds
	if (tempPlayer)
		flashDuration = FLASHDURATION;
}

MovingObject::~MovingObject()
{
	delete myEmitter;
}

/**************************************************************/
// Update
//	- move the entity's position by its velocity
/*virtual*/ void MovingObject::Update(float elapsedTime)
{
	Enemy* tempEnemy = dynamic_cast<Enemy*>(this);
	Player* tempPlayer = dynamic_cast<Player*>(this);

	if (damageTimer > 0)
		damageTimer -= elapsedTime;

	if (m_fSlowTimer > 0)
	{
		m_fSlowTimer -= elapsedTime;
		if (m_fSlowTimer < 0)
			m_fSlowTimer = 0;
	}
	if (m_fStunTimer > 0)
	{
		m_fStunTimer -= elapsedTime;
		if (m_fStunTimer < 0)
			m_fStunTimer = 0;
	}
	if (m_fDotTimer > 0)
	{
		m_fDotTimer -= elapsedTime;
		m_fDotDmgTimer -= elapsedTime;
		if (m_fDotDmgTimer < 0 && m_fDotTimer > 0)
		{
			m_fDotDmgTimer = 1.0f;

			m_iHealth -= 1;
		}
		if (m_fDotTimer < 0)
			m_fDotTimer = 0;

		//if (myEmitter != nullptr) delete myEmitter;
		//myEmitter = new Emitter(ParticleSystem::GetInstance()->GetEmitterMap()["SmokeyEffect"]);
		//myEmitter->SetObjectOwner(this);
		//myEmitter->Update(elapsedTime);
		m_ptPosition += (m_vtVelocity / 2) * elapsedTime;
		flashColor = { 128, 0, 128 };



	}

	if (gravityOn && GetType() != OBJ_BULLET)
	{
		m_vtVelocity += gravity * elapsedTime;
		if (m_fSlowTimer > 0) //slowed
		{
			m_ptPosition += (m_vtVelocity / 2) * elapsedTime;
			flashColor = { 128, 0, 128 };

			//if (myEmitter != nullptr) delete myEmitter;
			//myEmitter = new Emitter(ParticleSystem::GetInstance()->GetEmitterMap()["SludgeEffect"]);
			//myEmitter->SetObjectOwner(this);
			//myEmitter->Update(elapsedTime);

		}
		else
			m_ptPosition += m_vtVelocity * elapsedTime;
	}
	else if (m_fSlowTimer <= 0 && m_fStunTimer <= 0)
	{
		m_ptPosition += m_vtVelocity * elapsedTime;

	}
	else if (m_fSlowTimer > 0 || m_fStunTimer > 0)//THEY STUNNED OR SLOWED BRO
	{
		if (m_fSlowTimer > 0) //slowed
		{
			m_ptPosition += (m_vtVelocity / 2) * elapsedTime;
			flashColor = { 128, 0, 128 };

			//if (myEmitter != nullptr) delete myEmitter;
			//myEmitter = new Emitter(ParticleSystem::GetInstance()->GetEmitterMap()["SludgeEffect"]);
			//myEmitter->SetObjectOwner(this);
			//myEmitter->Update(elapsedTime);
		}
		else //stunned
		{
			flashColor = { 128, 0, 0, 255 };

			//if (myEmitter != nullptr) delete myEmitter;
			//myEmitter = new Emitter(ParticleSystem::GetInstance()->GetEmitterMap()["BlueSparkleEffect"]);
			//myEmitter->SetObjectOwner(this);
			//myEmitter->Update(elapsedTime);
		}
	}

	//update the damage flash timers
	if (damageFlash)
	{
		flashDuration -= elapsedTime;
		flashTimer -= elapsedTime;

		// how long objects will flash
		if (flashDuration <= 0.0f)
		{
			// set enemy flash duration / invulerability to .25 seconds
			flashDuration = FLASHDURATION;

			damageFlash = false;
			flashOn = true;
		}
		// how long each flash will last
		if (flashTimer <= 0.0f)
		{
			flashTimer = FLASHTIMER;
			flashOn = !flashOn;
		}
	}
	if (flashDuration <= 0.0f)
		damageFlash = false;

	animInfo.SetFlashOn((flashOn && damageFlash));

	if (animInfo.GetCurrAnimation() != ""  && AnimationSystem::GetInstance()->Contains(animInfo.GetCurrAnimation()))
	{
		Frame currFrame = AnimationSystem::GetInstance()->GetLoaded()[animInfo.GetCurrAnimation()].GetFrames()[animInfo.GetCurrFrame()];
		SetWeaponOffset(currFrame.GetWeaponOffset().x, currFrame.GetWeaponOffset().y);
	}

	AnimationSystem::GetInstance()->Update(animInfo, elapsedTime, this);
}

/**************************************************************/
// Render
//	- draw the entity's image at its position
void MovingObject::Render(void)
{
	//SGD::GraphicsManager::GetInstance()->DrawRectangle(GetRect(), m_cRect);

	if (damageFlash && flashOn)
	{
		if (m_fSlowTimer <= 0 && m_fStunTimer <= 0)
			flashColor = { 255, 0, 0 };

		SGD::GraphicsManager::GetInstance()->DrawRectangle({ GetRect().left - Game::GetInstance()->GetCameraPos().x,
			GetRect().top - Game::GetInstance()->GetCameraPos().y,
			GetRect().right - Game::GetInstance()->GetCameraPos().x,
			GetRect().bottom - Game::GetInstance()->GetCameraPos().y }, m_cRect, flashColor);
	}
	else
	{
		SGD::GraphicsManager::GetInstance()->DrawRectangle({ GetRect().left - Game::GetInstance()->GetCameraPos().x,
			GetRect().top - Game::GetInstance()->GetCameraPos().y,
			GetRect().right - Game::GetInstance()->GetCameraPos().x,
			GetRect().bottom - Game::GetInstance()->GetCameraPos().y }, m_cRect, { 255, 255, 255 });
	}



	// Draw the image
	//	SGD::GraphicsManager::GetInstance()->DrawTexture(
	//		m_hImage, m_ptPosition,
	//		m_fRotation, m_szSize);
}

/**************************************************************/
// GetRect
//	- calculate the entity's bounding rectangle
/*virtual*/ SGD::Rectangle MovingObject::GetRect(void) const
{
	//check if the current animation is set to anything
	if (animInfo.GetCurrAnimation() != "" && AnimationSystem::GetInstance()->Contains(animInfo.GetCurrAnimation()))
	{
		//shortcuts
		Frame currFrame = AnimationSystem::GetInstance()->GetLoaded()[animInfo.GetCurrAnimation()].GetFrames()[animInfo.GetCurrFrame()];
		SGD::Rectangle colRect = currFrame.GetCollisionRect();
		SGD::Point pt = currFrame.GetAnchorPoint();

		//Return the current collision rectangle

		return{ SGD::Point(m_ptPosition.x - pt.x + colRect.left, m_ptPosition.y - pt.y + colRect.top),
			SGD::Size(colRect.ComputeWidth(), colRect.ComputeHeight()) };
	}
	return SGD::Rectangle{ SGD::Point(m_ptPosition.x - m_szSize.width / 2, m_ptPosition.y - m_szSize.height), m_szSize };
}

SGD::Rectangle MovingObject::GetActiveRect(void)	const
{
	if (animInfo.GetCurrAnimation() != "" && AnimationSystem::GetInstance()->Contains(animInfo.GetCurrAnimation()))
	{
		//shortcuts
		Frame currFrame = AnimationSystem::GetInstance()->GetLoaded()[animInfo.GetCurrAnimation()].GetFrames()[animInfo.GetCurrFrame()];
		SGD::Rectangle actRect = currFrame.GetActiveRect();
		SGD::Point pt = currFrame.GetAnchorPoint();

		//return the current active rectangle
		return{ SGD::Point(m_ptPosition.x - pt.x + actRect.left, m_ptPosition.y - pt.y + actRect.top),
			SGD::Size(actRect.ComputeWidth(), actRect.ComputeHeight()) };

	}
	return SGD::Rectangle(0, 0, 0, 0);
}

/**************************************************************/
// HandleCollision
//	- respond to collision between entities
/*virtual*/ void MovingObject::HandleCollision(GameObject* pOther)
{
	if (pOther == this) return;
#pragma region pOther == bullet
	if (pOther->GetType() == OBJ_BULLET)
	{
		// cast as a bullet object
		const Projectile *bullet = dynamic_cast<const Projectile *>(pOther);

		
		// is the cast valid
		if (!bullet) return;

		// check to see if the owner is shooting themselves
		if (bullet->GetOwner() == this) return;

		// cast the bullets owner as an enemy
		const Enemy *enemyOwner = dynamic_cast<const Enemy*>(bullet->GetOwner());

		// if the owner is the player (and hasn't been damaged)
		if (!enemyOwner && damageTimer <= 0)
		{
			// cast the bullets owner as a player
			const Player* playerOwner = dynamic_cast<const Player*>(bullet->GetOwner());
			// if the owner is not the player either...
			if (!playerOwner) return;

			const Enemy* knight = dynamic_cast<Enemy*>(pOther);
			// 1/4 damage when knight is blocking
			if (knight && animInfo.GetCurrAnimation() == "knightattacking")
			{
				// reduce health by players damage score
				m_iHealth -= int(playerOwner->GetDamage() * .25f);
			}
			// regular damage
			else
			{
				// reduce health by players damage score
				m_iHealth -= playerOwner->GetDamage();
			}

			// set invulerability timer
			damageTimer = .25f;
			// make enemy flash when damaged
			damageFlash = true;
		}

		// if the owner IS an enemy but has not been damaged
		if (enemyOwner && damageTimer <= 0)
		{
			Player* player = GameplayState::GetInstance()->GetPlayer();

			// still shielded? reduce shields
			if (player->GetShieldCount() > 0)
				player->SetShieldCount(player->GetShieldCount() - 1);
			// no shields? take damage and flash
			else
			{
				// reduce health by enemies damage score
				m_iHealth -= enemyOwner->GetDamage();
				// set invulerability timer
				damageTimer = 1.0f;
				// make player flash when damaged
				damageFlash = true;
			}
		}
	}
#pragma endregion

	// player colliding with what??
	if (pOther->GetType() == MovingObject::OBJ_PLAYER)
	{
		if (gravityOn && GetType() != OBJ_BULLET)
		{
			m_vtVelocity -= gravity * dt;
			m_ptPosition -= m_vtVelocity * dt;
		}
		else
			m_ptPosition -= m_vtVelocity * dt;
	}
}

/**************************************************************/
// AddRef
//	- increase the reference count
/*virtual*/ void MovingObject::AddRef(void)
{
	++m_unRefCount;
}

/**************************************************************/
// Release
//	- decrease the reference count
//	- self-destruct when the count is 0
/*virtual*/ void MovingObject::Release(void)
{
	--m_unRefCount;

	if (m_unRefCount == 0)
		delete this;
}

void MovingObject::HandleEvent(const SGD::Event* pEvent)
{
	GameObject* object = (GameObject*)pEvent->GetSender();
	MovingObject* mObject = nullptr;
	if (pEvent->GetEventID() == "TILECOLLISION" && (mObject = dynamic_cast<MovingObject*>(object))) // The Event Should be sent if the collision happens from the tile system.
	{

		if (mObject->collisionNode.tileType == "Lava" && mObject->GetType() != Boss::BOSS_DRAGON)
		{
			if (!damageFlash)
			{
				mObject->SetHealth(mObject->GetHealth() - 50);
				mObject->damageFlash = true;
				mObject->flashDuration = 1.0f;
			}
		}

		if (mObject->collisionNode.tileType == "Boss" && mObject->GetType() == MovingObject::OBJ_PLAYER)
		{
			dynamic_cast<Player*>(mObject)->SetMinPosInWorld(mObject->collisionNode.collidedRect.right);
			return;
		}
		else if (mObject->collisionNode.tileType == "Boss")
			return;

		SGD::Rectangle intersection = mObject->collisionNode.collidedRect.ComputeIntersection(mObject->GetRect());

		SGD::Rectangle thisRect = GetRect();
		SGD::Rectangle otherRect = object->GetRect();

		if (thisRect.bottom - otherRect.top < intersection.ComputeWidth())
		{
			SetPosition({ GetPosition().x, GetPosition().y - (thisRect.bottom - otherRect.top) });
			SetVelocity({ GetVelocity().x, 0 });
			object->SetGravityState(false);
		}
		else if (otherRect.bottom - thisRect.top < intersection.ComputeWidth())
		{
			SetPosition({ GetPosition().x, GetPosition().y + (otherRect.bottom - thisRect.top) });
			SetVelocity({ GetVelocity().x, 0 });
		}

		if (thisRect.right - otherRect.left < intersection.ComputeHeight())
		{
			SetPosition({ GetPosition().x - (thisRect.right - otherRect.left), GetPosition().y });
			SetVelocity({ 0, GetVelocity().y });
		}
		else if (otherRect.right - thisRect.left < intersection.ComputeHeight())
		{
			SetPosition({ GetPosition().x + (thisRect.right - otherRect.left), GetPosition().y });
			SetVelocity({ 0, GetVelocity().y });
		}

		/*for (int i = 0; i < 3; i++)
		{
			SGD::Rectangle intersection = mObject->collisionNode.collidedRect.ComputeIntersection(mObject->GetRect());
			if ((intersection.ComputeHeight() / 32) < (intersection.ComputeWidth() / 32))
			{
				if (mObject->collisionNode.pastPosition.y < mObject->GetPosition().y &&
					object->GetRect().bottom >= intersection.top &&
					object->GetRect().bottom <= intersection.bottom)
				{
					HandleDownCollision(mObject, intersection);
				}
				else if (mObject->collisionNode.pastPosition.y > mObject->GetPosition().y &&
					object->GetRect().top >= intersection.top &&
					object->GetRect().top <= intersection.bottom)
				{
					HandleUpCollision(mObject, intersection);
				}
			}
			else
			{
				if (mObject->collisionNode.pastPosition.x > mObject->GetPosition().x &&
					object->GetRect().left >= intersection.left &&
					object->GetRect().left <= intersection.right &&
					mObject->collisionNode.tileType != "Climbing")
				{
					HandleLeftCollision(mObject, intersection);
				}
				if (mObject->collisionNode.pastPosition.x < mObject->GetPosition().x &&
					object->GetRect().right >= intersection.left &&
					object->GetRect().right <= intersection.right &&
					mObject->collisionNode.tileType != "Climbing")
				{
					HandleRightCollision(mObject, intersection);
				}
			}
		}*/
		//if (mObject->collisionNode.tileType == "Ground")
		//{
		//	HandleDownCollision(mObject, intersection);
		//	if (intersection.ComputeHeight() >= 16)
		//	{
		//		HandleLeftCollision(mObject, intersection);
		//		HandleRightCollision(mObject, intersection);
		//	}
		//}
		//else if (mObject->collisionNode.tileType == "Wall")
		//{
		//	HandleLeftCollision(mObject, intersection);
		//	HandleRightCollision(mObject, intersection);
		//}
		//else if (mObject->collisionNode.tileType == "Ceiling")
		//	HandleUpCollision(mObject, intersection);
		//else if (mObject->collisionNode.tileType == "Top-Left")
		//{
		//	HandleUpCollision(mObject, intersection);
		//	HandleLeftCollision(mObject, intersection);
		//}
		//else if (mObject->collisionNode.tileType == "Top")
		//{
		//	HandleUpCollision(mObject, intersection);
		//}
		//else if (mObject->collisionNode.tileType == "Top-Right")
		//{
		//	HandleUpCollision(mObject, intersection);
		//	HandleRightCollision(mObject, intersection);
		//}
		//else if (mObject->collisionNode.tileType == "Left")
		//{
		//	HandleLeftCollision(mObject, intersection);
		//}
		//else if (mObject->collisionNode.tileType == "Right")
		//{
		//	HandleRightCollision(mObject, intersection);
		//}
		//else if (mObject->collisionNode.tileType == "Bottom-Left")
		//{
		//	HandleDownCollision(mObject, intersection);
		//	HandleLeftCollision(mObject, intersection);
		//}
		//else if (mObject->collisionNode.tileType == "Bottom")
		//{
		//	HandleDownCollision(mObject, intersection);
		//}
		//else if (mObject->collisionNode.tileType == "Bottom-Right")
		//{
		//	HandleDownCollision(mObject, intersection);
		//	HandleRightCollision(mObject, intersection);
		//}


		object = nullptr;
	}
	if (pEvent->GetEventID() == "LIFESTEAL")
	{
			int bulletdmg = *(int*)pEvent->GetData();
			MovingObject* tempobj = (MovingObject*)pEvent->GetSender();
			if (tempobj->GetHealth() < tempobj->GetMaxHealth())
				tempobj->SetHealth(tempobj->GetHealth() + bulletdmg/2);
			if (tempobj->GetHealth() > tempobj->GetMaxHealth())
				tempobj->SetHealth(tempobj->GetMaxHealth());
	}
	if (pEvent->GetEventID() == "DOT")
	{
		MovingObject* tempobj = (MovingObject*)pEvent->GetData();
		tempobj->m_fDotTimer = 3.0;
		tempobj->m_fDotDmgTimer = 1.0f;
	}
	if (pEvent->GetEventID() == "STUN")
	{
		MovingObject* tempobj = (MovingObject*)pEvent->GetData();
		tempobj->m_fStunTimer = .50;
	}
	if (pEvent->GetEventID() == "SLOW")
	{
		MovingObject* tempobj = (MovingObject*)pEvent->GetData();
		tempobj->m_fSlowTimer = 1.0;
	}
	if (pEvent->GetEventID() == "KNOCKBACK")
	{
		MovingObject* tempobj = (MovingObject*)pEvent->GetData();
		SGD::Point pos = tempobj->GetPosition();
		SGD::Vector vec = tempobj->GetVelocity();
		if (vec.x > 0)
			pos.x -= 4;
		else
			pos.x += 4;
		tempobj->SetPosition(pos);
	}

}

void MovingObject::SetCollisionData(SGD::Rectangle collisionRect, SGD::Point pPos, std::string tileType)
{
	collisionNode.collidedRect = collisionRect;
	collisionNode.pastPosition = pPos;
	collisionNode.tileType = tileType;
}

void MovingObject::HandleRightCollision(MovingObject* object, SGD::Rectangle intersection)
{
	CollisionData collisionData = *object->GetCollisionData();
	object->SetPosition({ intersection.left - (object->GetRect().ComputeWidth() * .5f) - 5, object->GetPosition().y });
	object->SetVelocity({ 0, object->GetVelocity().y });
}

void MovingObject::HandleLeftCollision(MovingObject* object, SGD::Rectangle intersection)
{
	CollisionData collisionData = *object->GetCollisionData();
	object->SetPosition({ intersection.right + (object->GetRect().ComputeWidth() * .5f), object->GetPosition().y });
	object->SetVelocity({ 0, object->GetVelocity().y });
}

void MovingObject::HandleUpCollision(MovingObject* object, SGD::Rectangle intersection)
{
	CollisionData collisionData = *object->GetCollisionData();
	object->SetPosition({ object->GetPosition().x, object->GetRect().bottom + intersection.ComputeHeight() + 1 });
	object->SetVelocity({ object->GetVelocity().x, 0 });
}

void MovingObject::HandleDownCollision(MovingObject* object, SGD::Rectangle intersection)
{
	CollisionData collisionData = *object->GetCollisionData();
	object->SetPosition({ object->GetPosition().x, intersection.top });
	object->SetVelocity({ object->GetVelocity().x, 0 });
	object->SetGravityState(false);
}
void MovingObject::ClearDebuffs()
{
	m_fDotDmgTimer = 0;
	m_fDotTimer = 0;
	m_fSlowTimer = 0;
	m_fStunTimer = 0;
}